#!/usr/bin/php
<?php
/* Copyright 2015, Guilherme Jardim
 * Copyright 2016-2021, Dan Landon
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */

$plugin = "unassigned.devices";
$docroot = $docroot ?: @$_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once("$docroot/plugins/{$plugin}/include/lib.php");

$COMMAND = $argv[1];
if ($COMMAND != 'spindown') {
	$DEVNAME = (isset($_ENV['DEVNAME'])) ? $_ENV['DEVNAME'] : ( isset($argv[2]) ? $argv[2] : NULL );
	$DEVNAME = (file_exists($DEVNAME)) ? realpath($DEVNAME) : $DEVNAME;
} else {
	$DEVNAME = $argv[2];
}
$remove  = [];
if (! $DEVNAME) die("Fail: device not defined.\n");

function unassigned_mount() {
	global $DEVNAME, $paths;

	# Mount local disks
	foreach(get_unassigned_disks() as $name => $disk) {
		$device = $disk['device'];
		foreach ($disk['partitions'] as $partition)
		{
			if ( preg_match("#".$DEVNAME."#i", realpath($partition)) || $DEVNAME == "auto" || $DEVNAME == "autodevices" ) {
				$info = get_partition_info($partition, true);
				if ($info['serial'] == "") {
					unassigned_log("Disk '{$device}' does not have a serial number and cannot be mounted.");
					continue;
				}
				if ( $info['pass_through'] ) {
					unassigned_log("Disk with serial '{$info['serial']}', mountpoint '".basename($info['mountpoint'])."' is set as passed through.");
					continue;
				}
				if (! $info['automount'] && ( $DEVNAME == "auto" || $DEVNAME == "autodevices" || isset($_ENV['DEVNAME']) )) {
					unassigned_log("Disk with serial '{$info['serial']}', mountpoint '".basename($info['mountpoint'])."' is not set to auto mount.");
					setSleepTime($device);
					continue;
				}
				if ($info['label'] == "UNRAID") {
					unassigned_log("Error: Cannot mount device '{$info['device']}' with label 'UNRAID'.");
				} elseif ($info['device'] != "") {
					unassigned_log("Disk found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
					unassigned_log("Adding disk '{$info['device']}'...");
					if ($info['fstype'] != "crypto_LUKS") {
						addFile(sprintf($paths['mounting'],basename($info['device'])));
					} else {
						addFile(sprintf($paths['mounting'],basename($info['luks'])));
					}
					if (do_mount( $info )) 
					{
						$attrs = (isset($_ENV['DEVTYPE'])) ? get_udev_info($device, $_ENV, $reload) : get_udev_info($device, NULL, $reload);
						if (config_shared( $info['serial'], $info['part'], strpos($attrs['DEVPATH'],"usb"))) {
							add_smb_share($info['mountpoint'], $info['label']);
							add_nfs_share($info['mountpoint']);
						}
						setSleepTime($device);
						execute_script($info, "ADD");
						/* Update the partition info */
						$info = get_partition_info($partition, true);
						export_disk($info, true);
					} elseif (! is_mounted($info['device'])) {
						unassigned_log("Partition '{$info['label']}' cannot be mounted.");
						setSleepTime($device);
						execute_script($info, "ERROR_MOUNT");
					}
				} else {
					unassigned_log("Error: Cannot mount null device with serial '{$info['serial']}'.");
				}
			}
		}
	}

	# Mount Remote SMB/NFS mounts
	if (strpos($DEVNAME, "//") === 0 || strpos($DEVNAME, ":/") || $DEVNAME == "auto" || $DEVNAME == "autoshares") {
		foreach (get_samba_mounts() as $info) {
			$device = $info['device'];
			if ( $DEVNAME == $device || $DEVNAME == "auto" || $DEVNAME == "autoshares" ) {
				if (! $info['automount'] && ($DEVNAME == "auto" || $DEVNAME == "autoshares")) {
					unassigned_log("Remote SMB/NFS mount '{$info['device']}' is not set to auto mount.");
					continue;
				}
				unassigned_log("Remote SMB/NFS share found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
				/* Remove special characters */
				$mount_device = str_replace( array("(", ")"), "", basename($info['device']));
				addFile(sprintf($paths['mounting'],$mount_device));
				if (do_mount( $info )) {
					if ($info['smb_share']) {
						add_smb_share($info['mountpoint'], $info['device'], FALSE);
						/* Update the samba mount status */
						foreach (get_samba_mounts() as $info) {
						$device = $info['device'];
							if ( $DEVNAME == $device ) {
								export_disk($info, true);
								break;
							}
						}
					}
					execute_script($info, "ADD");

				} else {
					execute_script($info, "ERROR_MOUNT");
				}
			}
		}
	}

	# Mount ISO File mounts
	if (strpos($DEVNAME, "/mnt") === 0 || $DEVNAME == "auto" || $DEVNAME == "autodevices") {
		foreach (get_iso_mounts() as $info) {
			$device = $info['device'];
			if ( $DEVNAME == $device || $DEVNAME == "auto" || $DEVNAME == "autodevices" ) {
				if (! $info['automount'] && ($DEVNAME == "auto" || $DEVNAME == "autodevices")) {
					unassigned_log("ISO File mount '{$info['device']}' is not set to auto mount.");
					continue;
				}
				unassigned_log("ISO File share found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
				addFile(sprintf($paths['mounting'],basename($info['device'])));
				if (do_mount( $info )) {
					add_smb_share($info['mountpoint'], $info['device'], FALSE);
					add_nfs_share($info['mountpoint']);
					execute_script($info, "ADD");
					/* Update the iso mount status */
					foreach (get_iso_mounts() as $info) {
						$device = $info['device'];
						if ( $DEVNAME == $device ) {
							export_disk($info, true);
							break;
						}
					}
				} else {
					execute_script($info, "ERROR_MOUNT");
				}
			}
		}
	}

	/* Tell Unraid to update devs.ini file of unassigned devices. */
	if (isset($_ENV['DEVTYPE']) && ($_ENV['DEVTYPE'] == "partition")) {
		$tc = $paths['hotplug_status'];
		$hotplug = is_file($tc) ? json_decode(file_get_contents($tc),TRUE) : "no";
		if ($hotplug == "no") {
			file_put_contents($tc, json_encode('yes'));
		}
	}
}

function unassigned_umount() {
	global $DEVNAME, $paths;

	$force = ($DEVNAME == "all") ? TRUE : FALSE;

	# Deal with local disks
	foreach(get_unassigned_disks() as $disk) {
		$device = $disk['device'];
		foreach ($disk['partitions'] as $partition) {
			if ( preg_match("#".$DEVNAME."#i", realpath($partition)) || $DEVNAME == "auto" || $DEVNAME == "all" ) {
				$info = get_partition_info($partition, true);
				if (! $info['automount'] && $DEVNAME == "auto" ) {
					continue;
				}
				unassigned_log("Drive found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
				if ($info['label'] == "UNRAID") {
					unassigned_log("Error: Cannot unmount device '{$info['device']}' with label 'UNRAID'.");
				} else {
					if ( is_mounted($info['device']) )
					{
						addFile(sprintf($paths['unmounting'],basename($info['device'])));
						setSleepTime($device);
						if ( rm_smb_share($info['target'], $info['label']) && rm_nfs_share($info['target']) ) {
							execute_script($info, "UNMOUNT");
							unassigned_log("Unmounting disk '{$info['label']}'...");
							if ( do_unmount($info['device'], $info['mountpoint']) ) {
								if ($info['fstype'] == "crypto_LUKS" ) {
									shell_exec("/sbin/cryptsetup luksClose ".basename($info['device']));
								}
								unassigned_log("Disk with serial '{$info['serial']}', mountpoint '".basename($info['mountpoint'])."' removed successfully.");
								execute_script($info, "REMOVE");
								export_disk($info, false);
							} else {
								unassigned_log("Disk '{$info['label']}' cannot be unmounted.");
								execute_script($info, "ERROR_UNMOUNT");
							}
						}
					}
				}
			}
		}
	}

	# Deal with Remote SMB/NFS mounts
	if (strpos($DEVNAME, "//") === 0 || strpos($DEVNAME, ":/") || $DEVNAME == "auto" || $DEVNAME == "all") {
		foreach (get_samba_mounts() as $info) {
			$device = $info['device'];
			if ( $DEVNAME == $device || $DEVNAME == "auto" || $DEVNAME == "all" ) {
				if (! $info['automount'] && $DEVNAME == "auto" ) {
					continue;
				}
				unassigned_log("Remote SMB/NFS share found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
				$force = $info['is_alive'] ? $force : TRUE;
				if ( is_mounted(($info['fstype'] == "cifs") ? "//".$info['ip']."/".$info['path'] : $info['device']) ) {
					addFile(sprintf($paths['unmounting'],basename($info['device'])));
					unassigned_log("Removing Remote SMB/NFS share '{$info['device']}'...");
					execute_script($info, "UNMOUNT");
					unassigned_log("Unmounting Remote SMB/NFS Share '{$info['device']}'...");
					$smb = $info['fstype'] == "cifs" ? TRUE : FALSE;
					$nfs = $info['fstype'] == "nfs" ? TRUE : FALSE;
					if ( do_unmount(($info['fstype'] == "cifs") ? "//".$info['ip']."/".$info['path'] : $info['device'], $info['mountpoint'], $force, $smb, $nfs) ) {
						if ( rm_smb_share($info['mountpoint'], $info['device']) ) {
							unassigned_log("Share '{$info['device']}' unmount successfull.");
							execute_script($info, "REMOVE");
							export_disk($info, false);
						}
					} else {
						execute_script($info, "ERROR_UNMOUNT");
					}
				} else {
					unassigned_log("Remote SMB/NFS share '{$info['device']}' is not mounted.");
				}
			}
		}
	}

	# Deal with ISO File mounts
	if (strpos($DEVNAME, "/mnt") === 0 || $DEVNAME == "auto" || $DEVNAME == "all") {
		foreach (get_iso_mounts() as $info) {
			$device = $info['device'];
			if ( $DEVNAME == $device || $DEVNAME == "auto" || $DEVNAME == "all" ) {
				if (! $info['automount'] && $DEVNAME == "auto" ) {
					continue;
				}
				unassigned_log("ISO File share found with the following attributes: ".(implode(', ', array_map(function($v, $k){$v = (strpos($k, "pass") !== false) ? "*******" : $v; return "$k='$v'"; }, $info, array_keys($info)))), "DEBUG");
				if ( is_mounted($info['device']) ) {
					addFile(sprintf($paths['unmounting'],basename($info['device'])));
					unassigned_log("Removing ISO File share '{$info['device']}'...");
					execute_script($info, "UNMOUNT");
					unassigned_log("Unmounting ISO File Share '{$info['device']}'...");
					if ( do_unmount($info['device'], $info['mountpoint'], $force) ) {
						if ( rm_smb_share($info['mountpoint'], $info['device']) && rm_nfs_share($info['mountpoint']) ) {
							unassigned_log("Share '{$info['device']}' unmount successfull.");
							execute_script($info, "REMOVE");
							export_disk($info, false);
						}
					} else {
						execute_script($info, "ERROR_UNMOUNT");
					}
				} else {
					unassigned_log("Remote ISO File share '{$info['device']}' is not mounted.");
				}
			}
		}
	}

	/* Tell Unraid to update devs.ini file of unassigned devices. */
	if (isset($_ENV['DEVTYPE']) && ($_ENV['DEVTYPE'] == "disk")) {
		$tc = $paths['hotplug_status'];
		$hotplug = is_file($tc) ? json_decode(file_get_contents($tc),TRUE) : "no";
		if ($hotplug == "no") {
			file_put_contents($tc, json_encode('yes'));
		}
	}
}

function unassigned_reload() {
	if (isset($_ENV['DEVLINKS'])) {
		foreach (explode(" ", $_ENV['DEVLINKS']) as $link) {
			get_udev_info($link, $_ENV, true);
		}
	}
}

function unassigned_spin_down() {
	global $DEVNAME;

	$dev = basename($DEVNAME);
	spin_disk(TRUE, $dev);
}

function addFile($file)
{
	global $remove;

	@touch($file);
	$remove[] = $file;
}

function export_disk($disk, $add) {
	global $paths;

	$info = MiscUD::get_json($paths['mounted']);
	$dev  = $disk['device'];
	if ($add)
	{
		if (isset($disk["pass"])) {
			unset($disk["pass"]);
		}
		$info[$dev] = $disk;
	}
	else
	{
		unset($info[$dev]);
	}
	$info = MiscUD::save_json($paths['mounted'], $info);	
}

switch ($COMMAND) {
	case 'mount':
		unassigned_mount();
		break;

	case 'umount':
		unassigned_umount();
		break;

	case 'reload':
		unassigned_reload();
		break;

	case 'spindown':
		unassigned_spin_down();
		break;

	case 'refresh':
		break;

	default:
		unassigned_log("Error: 'rc.unassigned {$argv[1]} {$argv[2]}' not understood");
		unassigned_log("rc.unassigned usage: 'mount','umount','reload', 'spindown', 'refresh'");
		exit(0);
		break;
}

array_map(function($f){@unlink($f);}, $remove);
publish("reload", json_encode(array("rescan" => "yes"),JSON_UNESCAPED_SLASHES));
?>
